#!/usr/bin/env python
import pygame
from pygame.locals import *

import math
import os.path

from pgEngine.boat import Boat
from pgEngine.fish import SeaCreature
from pgEngine.pirates import Pirate
from pgEngine.island import Island
from pgEngine.point import Point

from colors import *
from resources import Resources

tileLoc = lambda x, y, s: (x*s[0] + s[2], y*s[1] + s[3], s[4], s[5])

def translate(points, by):
    return [ (point[0] + by[0], point[1] + by[1])
             for point in points ]

class MapWindow:
    rcMap = None
    mapSize = None
    mapPos = None
    
    oldPos = None
    oldObjPos = None
    
    tilePos = None
    tileOffX = None
    tileOffY = None
    
    gridSize = None
    tileSize = None
    gridRatio = None
    halfGrid = None

    mouseCol = None
    mouseRow = None
    
    mainWindow = None
    res = None
    realscreen = None
    screen = None
    oldMap = None
    
    mapTiles = None
    mapTileImage = None
    tiles = None
    
    highlight = None
    
    def __init__(self, rcMap, res=None, mainWindow=None):
        self.rcMap = rcMap
        self.mapSize = Point(*rcMap.size)
        self.screen = pygame.Surface(self.mapSize.pos,RLEACCEL)
        self.realscreen = pygame.display.get_surface()
        if res is None:
            res = Resources()
        self.res = res
        self.gridSize = Point(24,24)
        self.halfGrid = Point(12,12)
        self.mapPos = Point(0.0, 0.0)
        self.oldPos = Point(1000000,1000000)
        self.oldObjPos = ()
        self.mainWindow = mainWindow
        self.dirty = False
        
    def resize(self, rcMap):
        self.rcMap = rcMap
        self.mapSize = Point(*rcMap.size)
        self.screen = pygame.Surface(self.mapSize.pos)
        self.dirty = True

    def getMousePos(self, screen=False, grid=True):
        mouseX,mouseY = self.mainWindow.mouseX, self.mainWindow.mouseY
        assert not screen
        if grid:        # Game coordinate system
            self.mouseCol = float(mouseX - self.rcMap.left)
            self.mouseCol /= float(self.gridSize.x)
            self.mouseRow = float(self.rcMap.bottom - mouseY)
            self.mouseRow /= float(self.gridSize.y)
            return self.mouseCol + self.mapPos.x, self.mouseRow + self.mapPos.y

        else:           # Tile-background coordinate system
            windowX = mouseX - self.rcMap.left
            windowY = self.rcMap.bottom - mouseY
            screenX = float(windowX) / float(self.tileSize.x)
            screenY = float(windowY) / float(self.tileSize.y)
            self.mouseCol = math.floor(screenX + self.tilePos.x)
            self.mouseRow = math.floor(screenY + self.tilePos.y)
            return (self.mouseCol, self.mouseRow)

    def getScreenPos(self, cell, center=False):
        """ Return the top-left corner pixel position
            for the specified cell.  Relative to rcMap. """
        tile = cell / self.gridRatio
        tileFloor = tile.floor
        cellFloor = tileFloor * self.gridRatio
        cellOff = cell - cellFloor
        tileLookup = Point(*tileFloor.pos) + Point(0,-1)
        tilePos = self.getTilePos(tileLookup)
        cellOff = cellOff * self.gridSize * Point(1,-1)
        if center:
            center += self.gridSize / 2
        return tilePos + cellOff + Point(0,1)
    
    def getTilePos(self, tile):
        """ Return the top-left corner pixel position
            for the specified tile.  Relative to rcMap. """
        tilePosFloor = self.tilePos.floor
        offset = (self.tilePos - tilePosFloor) * self.tileSize
        firstTile = tilePosFloor - Point(1.0, 1.0)
        firstTilePos = Point(-self.tileSize.x - offset.x,
                             self.mapSize.y + offset.y)
        tileSize = self.tileSize * Point(1.0, -1.0)
        return (tile - firstTile) * tileSize + firstTilePos
    
    def getCenterTile(self):
        tilePosFloor = self.tilePos.floor
        tiles = self.mapSize / self.tileSize
        return tilePosFloor + tiles / 2

    def createTiles(self):
        self.mapTiles = {}

        tileSize = (51,51,2,2,48,48)
        self.tileSize = Point(*tileSize[4:6])
        self.gridRatio = Point(2.0, 2.0)
        self.mapTiles["water"] = []
        self.mapTileImage = self.res.loadImage("maptiles.png")
        # A list for animated tiles.
        getTile = lambda x, y: [ self.mapTileImage.subsurface(
                                    tileLoc(x,y, tileSize)) ]
        self.tiles = {
            "water3":      (0,0,0),
            "water2":      (1,0,0),
            "water" :      (2,0,0),
            "cldtwge1":    (3,0,0),
            "cldtwge2":    (4,0,0),
            "cldtwgn1":    (5,0,0),
            "cldtwgn2":    (6,0,0),
            "cldtggn1":    (7,0,0),
            "cldtgwnw":    (0,1,0),
            "cldtgwne":    (1,1,0),
            "cldtwgnw":    (2,1,0),
            "cldtwgne":    (3,1,0),
            "cldtwgw1":    (4,1,0),
            "cldtwgs1":    (5,1,0),
            "cldtwgs2":    (6,1,0),
            "cldtggn2":    (7,1,0),
            "cldtgwsw":    (0,2,0),
            "cldtgwse":    (1,2,0),
            "cldtwgsw":    (2,2,0),
            "cldtwgse":    (3,2,0),
            "cldtwgw2":    (4,2,0),
            "cldtggw1":    (5,2,0),
            "cldtggw2":    (6,2,0),
            "cldtgge1":    (7,2,0),
            "cldtggnw1":   (0,3,0),
            "cldtggne1":   (1,3,0),
            "cldtggnw2":   (2,3,0),
            "cldtggne2":   (3,3,0),
            "cldtggn3":    (4,3,0),
            "cldtggn4":    (5,3,0),
            "cldtggw3":    (6,3,0),
            "cldtgge2":    (7,3,0),
            "cldtggsw1":   (0,4,0),
            "cldtggse1":   (1,4,0),
            "cldtggsw2":   (2,4,0),
            "cldtggse2":   (3,4,0),
            "cldtggn5":    (4,4,0),
            "cldtggn6":    (5,4,0),
            "cldtggw4":    (6,4,0),
            "watermound":  (7,4,0),
            "clstwrse1":   (0,5,0),
            "clstwrs1":    (1,5,0),
            "clstwrsw1":   (2,5,0),
            "waterpost":   (3,5,0),
            "cldtggn7":    (4,5,0),
            "cldtggn8":    (5,5,0),
            "cldtgge3":    (6,5,0),
            "cldtgge4":    (7,5,0),
            "clstwre":     (0,6,0),
            "clstwrn1":    (1,6,0),
            "clstwrw":     (2,6,0),
            "clstwrse2":   (3,6,0),
            "clstwrsw2":   (4,6,0),
            "docks1":      (5,6,0),
            "docks2":      (6,6,0),
            "docks3":      (7,6,0),
            "clstwrne1":   (0,7,0),
            "clstwrn2":    (1,7,0),
            "clstwrnw1":   (2,7,0),
            "clstwrne2":   (3,7,0),
            "clstwrnw2":   (4,7,0),
            "dockn1":      (5,7,0),
            "dockn2":      (6,7,0),
            "dockn3":      (7,7,0)
        }
        
        
        for page in range(1, 11):
            for row in range(8):
                if page == 10 and row > 5:
                    break
                for col in range(8):
                    subpage = 4 * page + 2 * (row / 4) + col / 4
                    tileName = "p%ir%ic%i" % (subpage, row, col)
                    prow = 8 * (page/2) + row
                    pcol = 8 * (page%2) + col
                    self.tiles[tileName] = (pcol,prow,page)
        
        for tile in self.tiles:
            self.mapTiles[tile] = getTile(*self.tiles[tile][:2])

        boats = { 1: "s",
                  3: "sw",
                  5: "w",
                  7: "nw",
                  9: "n",
                  11: "ne",
                  13: "e",
                  15: "se" }
            
        for boat in boats:
            boatName = "boat2-%s" % boats[boat]
            fBoat = os.path.join("galleon", "%04i.png" % boat)
            mapTile = self.res.loadImage(fBoat)
            #mapTile = pygame.image.load(fBoat)
            self.mapTiles[boatName] = [
                    pygame.transform.rotozoom(mapTile, 0, 0.15)
                    ]
        
        boats = { 1: "w",
                  3: "nw",
                  5: "n",
                  7: "ne",
                  9: "e",
                  11: "se",
                  13: "s",
                  15: "sw" }
                  
        for boat in boats:
            boatName = "boat1-%s" % boats[boat]
            fBoat = os.path.join("trireme", "%04i.png" % boat)
            mapTile = self.res.loadImage(fBoat)
            self.mapTiles[boatName] = [mapTile]

        
        
        # Create the highlights
        self.highlight = self.createHighlight(self.gridSize.pos)



    def createHighlight(self, gridSize):        
        highlight = pygame.Surface((gridSize[0]+5, gridSize[1]+5))
        highlight.set_colorkey((0,0,0))
        gridPoints = [(3, 1),
                      (gridSize[0]+1, 1),
                      (gridSize[0]+1, gridSize[1]+1),
                      (1, gridSize[1]+1),
                      (1, 3)]
        pygame.draw.aalines(highlight, (255,255,255), False, gridPoints)
        highlight.set_at((2, 2), (1,)*3)
        return highlight
    
    def drawTiles(self, curT, world):
        if self.dirty or not self.tilePos == self.oldPos:
            self.oldMap = pygame.Surface(self.mapSize.pos)
            
            tileX, tileY = self.tilePos.x, self.tilePos.y
            mapW,mapH = self.mapSize.x, self.mapSize.y
            tileW,tileH = self.tileSize.pos
            tileCols = 4 + mapW / tileW
            tileRows = 4 + mapH / tileH
            firstCol = int(math.floor(tileX - 1))
            firstRow = int(math.floor(tileY - 1))
            offsetX = self.tileOffX = (tileX % 1.0) * tileW
            offsetY = self.tileOffY = (tileY % 1.0) * tileH
            drawX = -tileW - offsetX
            for col in range(firstCol, firstCol+tileCols):
                drawY = mapH + offsetY
                for row in range(firstRow, firstRow+tileRows):
                    tileName = world.tiles.get((col,row), "water")
                    tile = self.mapTiles[tileName][0]
                    self.oldMap.blit(tile, (drawX, drawY))
                    drawY -= tileH
                drawX += tileW
            
            self.oldPos = self.tilePos

        self.screen.blit(self.oldMap, (0,0))
            


            
    BOATDIR = {
        0: "boat%i-e",
        1: "boat%i-ne",
        2: "boat%i-n",
        3: "boat%i-nw",
        4: "boat%i-w",
        5: "boat%i-sw",
        6: "boat%i-s",
        7: "boat%i-se"
        }
    def drawMap(self, curT, world):
        mapCells = Point(2.0, 2.0) + (self.mapSize / self.gridSize)
        mapCols, mapRows = mapCells.pos
        x,y = self.mapPos.pos
        if self.dirty or (x,y,mapCols,mapRows) != self.oldObjPos:
            self.oldObjList = world.getItems(x, y, mapCols, mapRows)
            self.oldObjPos = (x, y,mapCols, mapRows)

        for loc, item in self.oldObjList:
            # Draw each item
            if isinstance(item, Boat):
                tile = self.mapTiles[
                        self.BOATDIR[item.direction.dir] % item.numericClass
                        ]
            elif isinstance(item, Pirate):
                tile = self.mapTiles["pirate"]

            # Find the cell            
            loc = self.getScreenPos(loc) + self.halfGrid
            loc -= Point(*tile[0].get_size()) / 2
            self.screen.blit(tile[0], loc.pos)
        

    def draw(self, curT, world, mapPos, layers=("Tiles", "Objects")):
        self.mapPos = mapPos
        self.tilePos = mapPos / self.gridRatio

        if "Tiles" in layers:
            self.drawTiles(curT, world)
        
        # After landscape, before objects
        self.mainWindow.modeDispatch("onDrawMapTiles", self.screen)

        if "Objects" in layers:
            self.drawMap(curT, world)
            
        self.dirty = False

        # After objects
        self.mainWindow.modeDispatch("onDrawMapObjects", self.screen)
            
        self.realscreen.blit(self.screen, self.rcMap)
